package com.googlecode.springgameengine.lwjgl;

import com.googlecode.springgameengine.resource.NativeResource;
import java.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.lwjgl.opengl.ARBFragmentShader;
import static org.lwjgl.opengl.ARBShaderObjects.*;
import org.lwjgl.opengl.ARBVertexShader;
import static org.lwjgl.opengl.GL11.*;

/**
 *
 * @author maciejsz
 */
public class LWJGLShaderMaterial extends NativeResource {
	
	private static final Log log = LogFactory.getLog(LWJGLShaderMaterial.class);
	
	private int vertexShaderHandle = 0;
	private int fragmentShaderHandle = 0;
	private int shaderHandle = 0;
	
	public LWJGLShaderMaterial(CharSequence vertexShaderCode, CharSequence fragmentShaderCode) throws ShaderLoadException {
		
		if((shaderHandle = glCreateProgramObjectARB()) == 0) throw new ShaderLoadException();
		if((vertexShaderHandle = createShader(vertexShaderCode, ARBVertexShader.GL_VERTEX_SHADER_ARB)) == 0) throw new ShaderLoadException();
		if((fragmentShaderHandle = createShader(fragmentShaderCode, ARBFragmentShader.GL_FRAGMENT_SHADER_ARB)) == 0) throw new ShaderLoadException();
		
		glAttachObjectARB(shaderHandle, vertexShaderHandle);
		glAttachObjectARB(shaderHandle, fragmentShaderHandle);
		
		glLinkProgramARB(shaderHandle);
		if(glGetObjectParameteriARB(shaderHandle, GL_OBJECT_LINK_STATUS_ARB) == GL_FALSE) throw new ShaderLoadException();
		
		glValidateProgramARB(shaderHandle);
		if(glGetObjectParameteriARB(shaderHandle, GL_OBJECT_VALIDATE_STATUS_ARB) == GL_FALSE) throw new ShaderLoadException();
	}
	
	public static int createShader(InputStream inputStream, int shaderType) throws ShaderLoadException, IOException {
		StringBuilder builder = new StringBuilder();
		try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
			String line;
			while((line = reader.readLine()) != null) {
				builder.append(line);
			}
		}

		return createShader(builder.toString(), shaderType);
	}

	public static int createShader(CharSequence vertexShader, int shaderType) throws ShaderLoadException {
		int handle;
		if((handle = glCreateShaderObjectARB(shaderType)) == 0) throw new ShaderLoadException();
		glShaderSourceARB(handle, vertexShader);
		glCompileShaderARB(handle);
		if(glGetObjectParameteriARB(handle, GL_OBJECT_COMPILE_STATUS_ARB) == GL_FALSE) throw new ShaderLoadException();
		return handle;
	}

	@Override
	public void unload() {
		if(shaderHandle != 0) {
			glDeleteObjectARB(shaderHandle);
			shaderHandle = 0;
		}
		if(vertexShaderHandle != 0) {
			glDeleteObjectARB(vertexShaderHandle);
			vertexShaderHandle = 0;
		}
		if(fragmentShaderHandle != 0) {
			glDeleteObjectARB(fragmentShaderHandle);
			fragmentShaderHandle = 0;
		}
	}

	@Override
	public boolean isLoaded() {
		return (shaderHandle | vertexShaderHandle | fragmentShaderHandle) != 0;
	}
	
	public int getShader() {
		return shaderHandle;
	}
	
	public static class ShaderLoadException extends Exception { };
}
